// Listing 16.5 Delegating to an Aggregated PartsList
#include <cstddef>
#include <iostream>
using namespace std;
// Abstract base class of parts
class Part
{
public:
    Part():itsPartNumber(1){}
    Part(int PartNumber):
        itsPartNumber(PartNumber){}
    virtual ~Part(){}
    int GetPartNumber() const
    { 
        return itsPartNumber;
    }    
    virtual void Display() const = 0;
private:
    int itsPartNumber;
};

// implementation of pure virtual function so that
// derived classes can chain up
void Part::Display() const
{
    cout << "\nPart Number: " << itsPartNumber << endl;
}

/**Car Part**/
class CarPart : public Part
{
public:
    CarPart():itsModelYear(94){}
    CarPart(int year, int partNumber);
    virtual void Display() const
    {
        Part::Display();
        cout << "Model Year: ";
        cout << itsModelYear << endl;
    }
private:
    int itsModelYear;
};

CarPart::CarPart(int year, int partNumber):
    itsModelYear(year),
    Part(partNumber)
{}

/**AirPlane Part**/
class AirPlanePart : public Part
{
public:
    AirPlanePart():itsEngineNumber(1){}
    AirPlanePart(int EngineNumber, int PartNumber);
    virtual void Display() const
    {
        Part::Display();
        cout << "Engine No.: ";
        cout << itsEngineNumber << endl;
    }
private:
    int itsEngineNumber;
};

AirPlanePart::AirPlanePart(int EngineNumber, int PartNumber):
    itsEngineNumber(EngineNumber),
    Part(PartNumber)
{}

/**Part Mode**/
class PartNode
{
public:
    PartNode(Part*);
    ~PartNode();
    void SetNext(PartNode *node)
    { itsNext = node; }
    PartNode *GetNext() const;
    Part *GetPart() const;
private:
    Part *itsPart;
    PartNode *itsNext;
};

// Partnode Implementing..
PartNode::PartNode(Part* pPart):
    itsPart(pPart),
    itsNext(0)
{}

PartNode::~PartNode()
{
    delete itsPart;
    itsPart = 0;
    delete itsNext;
    itsNext = 0;
}

// Return NULL if no next PartNode
Part* PartNode::GetPart() const
{
    if(itsPart)
    {
        return itsPart;
    }
    else 
    {
        return NULL;   // error
    }
}

/**Part LIst**/
class PartList
{
public:
    PartList();
    ~PartList();
    // needs copy constructor and operator equals!
    void Iterate(void (Part::*f)()const) const;
    Part* Find(int& position, int PartNumber) const;
    Part* GetFirst() const;
    void Insert(Part*);
    Part* operator[](int) const;
    int GetCount() const{ return itsCount; }
    static PartList& GetGlobalPartList()
    {
        return GlobalPartList;
    }
private:
    PartNode *pHead;
    int itsCount;
    static PartList GlobalPartList;
};

PartList PartList::GlobalPartList;

PartList::PartList():
    pHead(0),
    itsCount(0)
{}

PartList::~PartList()
{
    delete pHead;
}

Part* PartList::GetFirst() const
{
    if(pHead)
    {
        return pHead->GetPart();
    }
    else
    {
        return NULL;    // error catch here
    }
}

Part* PartList::operator[](int offSet) const
{
    PartNode* pNode = pHead;
    if(!pHead)
    {
        return NULL;    // error catch here
    }
    if(offSet > itsCount)
    {
        return NULL;    // error
    }

    for(int i = 0; i < offSet; i++)
    {
        pNode = pNode->GetNext();
    }
    return pNode->GetPart();
}

Part* PartList::Find(int& position, int PartNumber) const
{
    PartNode* pNode = 0;
    for(pNode= pHead, position = 0;
        pNode != NULL;
        pNode = pNode->GetNext(),position++)
    {
        if(pNode->GetPart()->GetPartNumber() == PartNumber)
        {
            break;
        }
    }    
    if(pNode == NULL)
    {
        return NULL;
    }
    else
    {
        return pNode->GetPart();
    }
}

void PartList::Iterate(void (Part::*func)() const) const
{
    if(!pHead)
    {
        return;
    }
    PartNode* pNode = pHead;
    do
    {
        (pNode->GetPart()->*func)();
    }while((pNode = pNode->GetNext()) != 0);
}

void PartList::Insert(Part *pPart)
{
    PartNode* pNode = new PartNode(pPart);
    PartNode* pCurrent = pHead;
    PartNode* pNext = 0;

    int New = pPart->GetPartNumber();
    int Next = 0;
    itsCount++;

    if(!pHead)
    {
        pHead = pNode;
        return;
    }

    // if this one is smaller than head
    // this one is the new head
    if(pHead->GetPart()->GetPartNumber() > New)
    {
        pNode->SetNext(pHead);
        pHead = pNode;
        return;
    }

    for(;;)
    {
        // if there is no next, append this new one
        if(!pCurrent->GetNext())
        {
            pCurrent->SetNext(pNode);
            return;
        }

        // if thsi goes after this one and before the next
        // then insert it here, otherwise get the next
        pNext = pCurrent->GetNext();
        Next = pNext->GetPart()->GetPartNumber();
        if(Next > New)
        {
            pCurrent->SetNext(pNode);
            pNode->SetNext(pNext);
            return;
        }
        pCurrent = pNext;
    }
}

class PartsCatalog
{
public:
    void Insert(Part*);
    int Exists(int PartNumber);
    Part* Get(int PartNumber);
    operator+(const PartsCatalog&);
    void ShowAll()
    {
        thePartsList.Iterate(Part::Display);
    }
private:
    PartList thePartsList;
};
void PartsCatalog::Insert(Part *newPart)
{
    int partNumber = newPart->GetPartNumber();
    int offset;
    if(!thePartsList.Find(offset, partNumber))
    {
        thePartsList.Insert(newPart);
    }
    else
    {
        cout << partNumber << " was the ";
        switch(offset)
        {
            case 0:
                cout << "first ";
                break;
            case 1:
                cout << "second ";
                break;
            case 2:
                cout << "third ";
                break;
            default:
                cout << offset + 1 << "th ";            
        }
        cout << "entry, Rejected!" << endl;
    }
}

int PartsCatalog::Exists(int PartNumber)
{
    int offset;
    thePartsList.Find(offset,PartNumber);
    return offset;
}

Part* PartsCatalog::Get(int PartNumber)
{
    int offset;
    Part* thePart = thePartsList.Find(offset, PartNumber);
    return thePart;
}

int main(void)
{
    PartsCatalog pc;
    Part* pPart = 0;
    int PartNumber;
    int value;
    int choice = 99;

    while(choice != 0)
    {
        cout << "(0)Quit (1)Car (2)Plane: ";
        cin >> choice;
        if(choice != 0)
        {
            cout << "New PartNumber?: ";
            cin >> PartNumber;

            if(choice == 1)
            {
                cout << "Model Year?: ";
                cin >> value;
                pPart = new CarPart(value, PartNumber);
            }
             else
        {
            cout << "Engine Number?: ";
            cin >> value;
            pPart = new AirPlanePart(value, PartNumber);
        }
        pc.Insert(pPart);
        }
    }
    pc.ShowAll();
    return 0;
}





